/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill 庄骞 (smallchill@163.com)
 */
package org.springblade.modules.admin.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.dyvmsapi20170525.models.IvrCallRequest;
import com.aliyun.dyvmsapi20170525.models.IvrCallResponse;
import com.aliyun.dyvmsapi20170525.models.IvrCallResponseBody;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import org.springblade.common.constant.IvrVoiceConstant;
import org.springblade.common.mq.RescueDelaySend;
import org.springblade.common.utils.ivrcall.IvrCallUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.modules.admin.entity.*;
import org.springblade.modules.admin.enums.CallStatusEnum;
import org.springblade.modules.admin.enums.NoticeTaskStatusEnum;
import org.springblade.modules.admin.service.*;
import org.springblade.modules.admin.vo.RescueNoticeTaskVO;
import org.springblade.modules.admin.mapper.RescueNoticeTaskMapper;
import org.springblade.core.mp.base.BaseServiceImpl;
import org.springblade.modules.admin.vo.rescue.NotifyTemplateResult;
import org.springblade.modules.admin.vo.rescue.RoundUserResult;
import org.springblade.modules.system.service.IRoleMenuService;
import org.springblade.modules.system.service.IRoleService;
import org.springblade.modules.system.service.IUserService;
import org.springblade.modules.system.vo.SimpleUserVO;
import org.springblade.modules.system.vo.config.AlarmNotifyConfig;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 救援通知任务表 服务实现类
 *
 * @author ysq
 * @since 2025-07-10
 */
@Slf4j
@Service
public class RescueNoticeTaskServiceImpl extends BaseServiceImpl<RescueNoticeTaskMapper, RescueNoticeTask> implements IRescueNoticeTaskService {

	@Resource
	private IRescueNoticeService rescueNoticeService;
	@Resource
	private ElevatorPrincipalUserService elevatorPrincipalUserService;
	@Resource
	private ElevatorPrincipalDeptService elevatorPrincipalDeptService;
	@Resource
	private IRoleService roleService;
	@Resource
	private IUserService userService;
	@Resource
	private IRescueNoticeTaskDetailService rescueNoticeTaskDetailService;
	@Resource
	private IvrCallUtil ivrCallUtil;
	@Resource
	private Environment environment;
	@Resource
	private RescueDelaySend rescueDelaySend;
	@Resource
	private IRoleMenuService roleMenuService;

	/**
	 * 通知类型常量
	 */
	private static final int NOTIFY_TYPE_PRINCIPAL = 1; // 基于责任人
	private static final int NOTIFY_TYPE_ROLE = 2; // 基于角色

	/**
	 * 角色范围常量
	 */
	private static final int ROLE_SCOPE_ALL = 1; // 角色所有人
	private static final int ROLE_SCOPE_DEPT = 2; // 梯组所在的角色

	/**
	 * 未知电梯ID
	 */
	private static final Long UNKNOWN_ELEVATOR_ID = 0L;

	/**
	 * 人员调度code
	 */
	private static final String MENU_CODE = "user_dispatch";

	@Override
	public IPage<RescueNoticeTaskVO> selectRescueNoticeTaskPage(IPage<RescueNoticeTaskVO> page, RescueNoticeTaskVO rescueNoticeTask) {
		return page.setRecords(baseMapper.selectRescueNoticeTaskPage(page, rescueNoticeTask));
	}

	@Override
	public RescueNoticeTask getById(Long id) {
		return baseMapper.getById(id);
	}

	/**
	 * 主要流程说明
	 * - 报警事件触发，创建任务，开始第一轮通知。
	 * - 异步呼叫每个人，等待回调。
	 * - 回调处理：有一人打通则流程结束；否则继续等待。
	 * - 超时处理：每轮有超时时间，超时后自动进入下一轮。
	 * - 所有轮次都失败，则流程结束。
	 * @param task
	 * @param round 轮次
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void notifyRound(RescueNoticeTask task, int round) {
		RescueNotice rescueNotice = rescueNoticeService.getById(task.getRescueNoticeId());
		if (rescueNotice == null) {
			return;
		}
		if (rescueNotice.getConfirmType() != -1) {
			// 预警记录已确认，任务取消
			updateRescueNoticeTask(task.getId(), NoticeTaskStatusEnum.CANCEL);
			return;
		}

		// 检查是否已经处理过该轮次
		if (isRoundAlreadyProcessed(task.getId(), round)) {
			return;
		}

		RoundUserResult roundUserResult = getUsersForRound(rescueNotice, task, round);
		int actualRound = roundUserResult.getActualRound();
		List<SimpleUserVO> users = roundUserResult.getUsers();

		if (Func.isEmpty(users)) {
			// 没有可通知用户，直接结束任务
			updateRescueNoticeTask(task.getId(), NoticeTaskStatusEnum.FAIL);
			return;
		}

		// 根据角色获取对应通知模板
		NotifyTemplateResult notifyTemplate = this.getNotifyTemplate(roundUserResult.getType(), roundUserResult.getRoleAlias(), rescueNotice.getBuildingName(),
			rescueNotice.getElevatorAddress(), rescueNotice.getTenantId());

		// 第一步：先创建所有用户的详情记录
		List<RescueNoticeTaskDetail> allDetails = new ArrayList<>();
		for (SimpleUserVO user : users) {
			RescueNoticeTaskDetail detail = this.createRescueNotifyDetailEntity(task.getId(), actualRound, user);
			allDetails.add(detail);
		}

		// 批量保存所有详情记录
		rescueNoticeTaskDetailService.saveBatch(allDetails);

		for (int i = 0; i < users.size(); i++) {
			SimpleUserVO user = users.get(i);
			RescueNoticeTaskDetail detail = allDetails.get(i);
			// 请求呼叫
			IvrCallResponseBody responseBody = this.lvrCallRequest(user.getPhone(), detail.getId(), notifyTemplate);
			log.info("救援通知任务-请求呼叫结果， Body: {}", JSONUtil.toJsonStr(responseBody));
			if (responseBody != null && "OK".equals(responseBody.getCode())) {
				// 更新呼叫状态-已呼叫
				this.updateRescueNotifyDetail(detail.getId(), CallStatusEnum.CALLED, responseBody.getCallId(), null);
			} else {
				// 更新呼叫状态-未打通
				this.updateRescueNotifyDetail(detail.getId(), CallStatusEnum.NOT_CONNECT);
				// 检查本轮是否所有人都未打通
//				this.checkAndNotifyNextRoundIfNeeded(task, actualRound);
			}
		}
		// 记录本轮开始时间
		task.setRoundStartTime(DateUtil.date());
		// 超时兜底 60秒
		int timeout = 60;
		task.setRoundTimeout(timeout);
		this.updateById(task);

		//当前环境
		String curEnvironment = environment.getActiveProfiles()[0];
		// 设置延迟队列，超时后触发
		JSONObject delayMsg = new JSONObject();
		delayMsg.put("type", 1);
		delayMsg.put("taskId", task.getId());
		delayMsg.put("environment", curEnvironment);
		long sendTime =  DateUtil.date().getTime() + timeout * 1000;
		rescueDelaySend.sendDelayMsg(sendTime, delayMsg.toJSONString());
	}

	/**
	 * 检查轮次是否已经处理过
	 */
	private boolean isRoundAlreadyProcessed(Long taskId, int round) {
		List<RescueNoticeTaskDetail> details = rescueNoticeTaskDetailService.list(
			Wrappers.<RescueNoticeTaskDetail>lambdaQuery()
				.eq(RescueNoticeTaskDetail::getTaskId, taskId)
				.eq(RescueNoticeTaskDetail::getRound, round)
		);
		return !details.isEmpty();
	}

	/**
	 * 获取语音通知模板
	 * @param type 通知对象类型：1-责任人，2-角色
	 * @param roleAlias 角色别名
	 * @param buildingName 项目名称
	 * @param elevatorAddress 电梯地址
	 * @param tenantId 租户ID
	 * @return
	 */
	private NotifyTemplateResult getNotifyTemplate(Integer type, String roleAlias, String buildingName, String elevatorAddress, String tenantId) {
		String tts = IvrVoiceConstant.TTS_1;
		StringBuilder locationBuilder = new StringBuilder();
		if (Func.isNotBlank(buildingName)) {
			locationBuilder.append(buildingName);
		}
		if (Func.isNotBlank(elevatorAddress)) {
			locationBuilder.append(elevatorAddress);
		}

		Map<String, String> params = new HashMap<>(4);
		params.put("location", locationBuilder.toString());
		params.put("action", "前往救援");

		// 基于角色情况，根据调度权限，展示不同文案
		if (type == NOTIFY_TYPE_ROLE) {
			Long roleId = roleService.getRoleIdByAliases(tenantId, roleAlias);
			if (roleId != null) {
				// 拥有调度权限的roleId
				List<Long> roleIds = roleMenuService.selectMsgRole(MENU_CODE);
				if (roleIds.contains(roleId)) {
					params.put("action", "调度人员");
				}
			}
		}

		return new NotifyTemplateResult(tts, params);
	}

	@Override
	public void onCallResult(String body) {
		Map<String, Object> bodyMap = JSONUtil.toBean(body, HashMap.class);
		Long detailId = null;
		String statusCode = null;
		try {
			detailId = Long.valueOf(String.valueOf(bodyMap.get("out_id")));
			statusCode = (String) bodyMap.get("status_code");
		} catch (Exception e) {
			return;
		}

		RescueNoticeTaskDetail taskDetail = rescueNoticeTaskDetailService.getById(detailId);
		if (taskDetail == null) {
			return;
		}
		RescueNoticeTask task = this.getById(taskDetail.getTaskId());

		final String successCode = "200000";
		boolean isSuccess = statusCode.equals(successCode);
		// 更新呼叫状态
		updateRescueNotifyDetail(detailId, isSuccess ? CallStatusEnum.CONNECT : CallStatusEnum.NOT_CONNECT, null, body);
		if (isSuccess) {
			// finishTask 有人打通，结束任务
			updateRescueNoticeTask(task.getId(), NoticeTaskStatusEnum.COMPLETE);
		} else {
			// 检查当前轮次是否所有人都已处理完（通过回调或超时）
			checkRoundCompletion(task, taskDetail.getRound());
		}
	}

	/**
	 * 检查轮次是否完成
	 */
	private void checkRoundCompletion(RescueNoticeTask task, int round) {
		// 查询当前轮次的所有详情记录
		List<RescueNoticeTaskDetail> roundDetails = rescueNoticeTaskDetailService.list(
			Wrappers.<RescueNoticeTaskDetail>lambdaQuery()
				.eq(RescueNoticeTaskDetail::getTaskId, task.getId())
				.eq(RescueNoticeTaskDetail::getRound, round)
		);

		// 检查是否所有记录都有结果（已打通、未打通、已呼叫但未回调）
		boolean allProcessed = roundDetails.stream().allMatch(detail ->
			detail.getStatus() != CallStatusEnum.WAIT_CALL.getCode()
		);

		if (allProcessed) {
			// 检查是否所有人都未打通
			boolean allFailed = roundDetails.stream().allMatch(detail ->
				detail.getStatus() == CallStatusEnum.NOT_CONNECT.getCode()
			);

			if (allFailed) {
				// 当前轮次所有人都未打通，进入下一轮
				checkAndNotifyNextRoundIfNeeded(task, round, false);
			}
		}
		// 如果还有未处理的，等待回调或超时
	}

	/**
	 * 请求呼叫
	 * @param phone 手机号
	 * @param detailId 通知详情ID
	 * @return
	 */
	private IvrCallResponseBody lvrCallRequest(String phone, Long detailId, NotifyTemplateResult notifyTemplate) {
		IvrCallUtil.IvrCallRequestDto ivrCallRequestDto = new IvrCallUtil.IvrCallRequestDto();
		ivrCallRequestDto.setCalledNumber(phone);
		ivrCallRequestDto.setStartCode(notifyTemplate.getTts());
		ivrCallRequestDto.setStartTtsParams(notifyTemplate.getParams());
		ivrCallRequestDto.setTimeout(3000);

		List<IvrCallRequest.IvrCallRequestMenuKeyMap> list = new ArrayList<>();
		IvrCallRequest.IvrCallRequestMenuKeyMap keyMap1 = new IvrCallRequest.IvrCallRequestMenuKeyMap();
		keyMap1.setKey("1");
		keyMap1.setCode("9e20639f-7fb1-4a2a-9365-7745a91edf1a.wav");

		IvrCallRequest.IvrCallRequestMenuKeyMap keyMap2 = new IvrCallRequest.IvrCallRequestMenuKeyMap();
		keyMap2.setKey("2");
		keyMap2.setCode(notifyTemplate.getTts());
		keyMap2.setTtsParams(JSONUtil.toJsonStr(notifyTemplate.getParams()));

		list.add(keyMap1);
		list.add(keyMap2);
		ivrCallRequestDto.setMenuKeyMap(list);

		ivrCallRequestDto.setOutId(String.valueOf(detailId));

        try {
			IvrCallResponse response = ivrCallUtil.initiateIvrCall(ivrCallRequestDto);
			log.info("ivr-请求呼叫结果， Body: {}", JSONUtil.toJsonStr(response));
			if (response.getStatusCode() == 200) {
				return response.getBody();
			}
			return null;
        } catch (Exception e) {
           return null;
        }
    }

	/**
	 * 创建救援通知详情实体（不保存）
	 */
	private RescueNoticeTaskDetail createRescueNotifyDetailEntity(Long id, Integer round, SimpleUserVO user) {
		RescueNoticeTaskDetail taskDetail = new RescueNoticeTaskDetail();
		taskDetail.setTaskId(id);
		taskDetail.setRound(round);
		taskDetail.setUserId(user.getUserId());
		taskDetail.setPhone(user.getPhone());
		taskDetail.setStatus(CallStatusEnum.WAIT_CALL.getCode());
		return taskDetail;
	}

	/**
	 * 更新救援通知详情
	 * @param detailId 详情ID
	 * @param status 状态
	 * @param callId 通话ID
	 * @param resultBody 回调结果
	 */
	private void updateRescueNotifyDetail(Long detailId, CallStatusEnum status, String callId, String resultBody) {
		RescueNoticeTaskDetail taskDetail = new RescueNoticeTaskDetail();
		taskDetail.setId(detailId);
		taskDetail.setStatus(status.getCode());
		if (callId != null) {
			taskDetail.setCallId(callId);
		}
		if (resultBody != null) {
			taskDetail.setResultBody(resultBody);
		}
		rescueNoticeTaskDetailService.updateById(taskDetail);
	}

	/**
	 * 更新救援通知详情
	 * @param detailId id
	 * @param status 状态
	 */
	private void updateRescueNotifyDetail(Long detailId, CallStatusEnum status) {
		updateRescueNotifyDetail(detailId, status, null, null);
	}

	/**
	 * 更新救援通知任务
	 * @param taskId
	 * @param status
	 */
	private void updateRescueNoticeTask(Long taskId, NoticeTaskStatusEnum status) {
		RescueNoticeTask task = new RescueNoticeTask();
		task.setId(taskId);
		task.setStatus(status.getCode());
		this.updateById(task);
	}

	/**
	 * 获取用户列表
	 * @param rescueNotice
	 * @param task
	 * @param round
	 * @return
	 */
	private RoundUserResult getUsersForRound(RescueNotice rescueNotice, RescueNoticeTask task, int round) {
		Long elevatorId = rescueNotice.getElevatorId();
		AlarmNotifyConfig alarmNotifyConfig = task.getAlarmNotifyConfig();
		List<AlarmNotifyConfig.RoundConfig> rounds = alarmNotifyConfig.getRounds();

		for (int i = round; i <= rounds.size(); i++) {
			AlarmNotifyConfig.RoundConfig roundConfig = rounds.get(i - 1);
			if (!isRoundConfigApplicable(roundConfig, elevatorId)) {
				continue;
			}
			List<SimpleUserVO> users = getUsersByRoundConfig(roundConfig, rescueNotice, elevatorId);
			if (!Func.isEmpty(users)) {
				return new RoundUserResult(i, roundConfig.getType(), roundConfig.getRoleAlias(), users);
			}
		}
		return new RoundUserResult(-1,null,null, Collections.emptyList());
	}

	/**
	 * 检查轮次配置是否适用于当前电梯
	 */
	private boolean isRoundConfigApplicable(AlarmNotifyConfig.RoundConfig roundConfig, Long elevatorId) {
		// 如果电梯未知，则基于责任人或梯组角色的配置不适用
		if (UNKNOWN_ELEVATOR_ID.equals(elevatorId)) {
			return !(roundConfig.getType() == NOTIFY_TYPE_PRINCIPAL ||
					(roundConfig.getType() == NOTIFY_TYPE_ROLE && roundConfig.getScope() == ROLE_SCOPE_DEPT));
		}
		return true;
	}

	/**
	 * 根据轮次配置获取用户列表
	 */
	private List<SimpleUserVO> getUsersByRoundConfig(AlarmNotifyConfig.RoundConfig roundConfig,
													RescueNotice rescueNotice, Long elevatorId) {
		switch (roundConfig.getType()) {
			case NOTIFY_TYPE_PRINCIPAL:
				return getUsersByPrincipal(elevatorId);
			case NOTIFY_TYPE_ROLE:
				return getUsersByRole(roundConfig, rescueNotice, elevatorId);
			default:
				return Collections.emptyList();
		}
	}

	/**
	 * 基于责任人获取用户列表
	 */
	private List<SimpleUserVO> getUsersByPrincipal(Long elevatorId) {
		List<ElevatorPrincipalUser> principalUserList = elevatorPrincipalUserService.list(
			Wrappers.<ElevatorPrincipalUser>lambdaQuery()
				.eq(ElevatorPrincipalUser::getElevatorId, elevatorId)
		);

		if (Func.isEmpty(principalUserList)) {
			return Collections.emptyList();
		}

		List<Long> userIds = principalUserList.stream()
			.map(ElevatorPrincipalUser::getUserId)
			.collect(Collectors.toList());

		return userService.getSimpleUserInfoList(userIds);
	}

	/**
	 * 基于角色获取用户列表
	 */
	private List<SimpleUserVO> getUsersByRole(AlarmNotifyConfig.RoundConfig roundConfig,
											RescueNotice rescueNotice, Long elevatorId) {
		Long roleId = roleService.getRoleIdByAliases(rescueNotice.getTenantId(), roundConfig.getRoleAlias());
		if (roleId == null) {
			return Collections.emptyList();
		}

		switch (roundConfig.getScope()) {
			case ROLE_SCOPE_ALL:
				return userService.getUserByDeptAndRole(null, roleId);
			case ROLE_SCOPE_DEPT:
				return getUsersByDeptRole(elevatorId, roleId);
			default:
				return Collections.emptyList();
		}
	}

	/**
	 * 基于梯组角色获取用户列表
	 */
	private List<SimpleUserVO> getUsersByDeptRole(Long elevatorId, Long roleId) {
		ElevatorPrincipalDept elevatorPrincipalDept = elevatorPrincipalDeptService.getOne(
			Wrappers.<ElevatorPrincipalDept>lambdaQuery()
				.eq(ElevatorPrincipalDept::getElevatorId, elevatorId),
			false
		);

		if (elevatorPrincipalDept == null) {
			return Collections.emptyList();
		}

		return userService.getUserByDeptAndRole(elevatorPrincipalDept.getDeptId(), roleId);
	}


	/**
	 * 判断指定轮次是否所有人都未打通
	 * @param taskId 任务ID
	 * @param round 轮次
	 * @param isTimeout 是否为超时场景：true 表示只要不是打通(2)都视为未打通；false 表示仅未打通(3)计入失败
	 */
	private boolean allDetailsFailed(Long taskId, int round, boolean isTimeout) {
		List<RescueNoticeTaskDetail> details = rescueNoticeTaskDetailService.list(
			Wrappers.<RescueNoticeTaskDetail>lambdaQuery()
				.eq(RescueNoticeTaskDetail::getTaskId, taskId)
				.eq(RescueNoticeTaskDetail::getRound, round)
		);
		if (details.isEmpty()) {
			return false;
		}
		if (isTimeout) {
			// 超时：只要没有任何一条是打通(2)，则认定为本轮失败
			return details.stream().noneMatch(detail -> detail.getStatus() == CallStatusEnum.CONNECT.getCode());
		}
		// 回调：只有全部是未打通(3)才算失败
		return details.stream().allMatch(detail -> detail.getStatus() == CallStatusEnum.NOT_CONNECT.getCode());
	}

	/**
	 * 场景化的下一轮判定：回调/超时
	 */
	@Override
	public void checkAndNotifyNextRoundIfNeeded(RescueNoticeTask task, int currentRound, boolean isTimeout) {
		if (this.allDetailsFailed(task.getId(), currentRound, isTimeout)) {
			int nextRound = currentRound + 1;
			boolean hasNextRound = nextRound <= task.getAlarmNotifyConfig().getRounds().size();
			if (hasNextRound) {
				// 更新轮次
				this.update(Wrappers.<RescueNoticeTask>lambdaUpdate()
					.set(RescueNoticeTask::getCurrentRound, nextRound)
					.eq(RescueNoticeTask::getId, task.getId()));
				// 进入下一轮
				notifyRound(task, nextRound);
			} else {
				// finishTask
				updateRescueNoticeTask(task.getId(), NoticeTaskStatusEnum.FAIL);
			}
		}
	}

}
